home *** CD-ROM | disk | FTP | other *** search
- /*
- * scsi_commands.c: scsi command functions
- *
- * This is based on the file that NeXT included in
- * /NextDeveloper/Examples/UNIX/SCSI_CD,
- * done by James C. Lee at NeXT, Sep 1991, and updated by him
- * in Feb 1993. He, in turn, got a lot of data structures &
- * function calls from a perftest.c program done by Mike DeMoney.
- * It has been changed "just a bit" by Garance Alistair Drosehn/March 1994,
- * to add a few routines and make a few minor changes to some others.
- *
- */
-
- #import <sys/time.h>
- #import <bsd/dev/disk.h>
- #import <stdio.h>
- #import <libc.h>
- #import <stdlib.h>
- #import "scsi_commands.h"
-
- /* I use a copy of the NS-3.1 version of bsd/dev/scsireg.h, because
- * at some point I think I want to split it apart into "field values"
- * (such as #define DEVTYPE_DISK), and actual structure definitions.
- * Garance Drosehn/Mar 94
- */
- /* #import <bsd/dev/scsireg.h> */
- #import "scsireg_31.h" /* garance... */
-
-
- void fatal(const char *msg, ...)
- {
- va_list ap;
-
- va_start(ap, msg);
- /* fprintf(stderr, "%s: ", progname);*/
- vfprintf(stderr, msg, ap);
- fprintf(stderr, "\n");
- va_end(ap);
-
- exit(1);
- }
-
- inline int is_pow2(int i)
- {
- return (i & (i - 1)) == 0;
- }
-
- int do_inquiry(int fd, struct inquiry_reply *irp, struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_INQUIRY;
- c6p->c6_len = sizeof(*irp);
-
- sr.sr_addr = (char *)irp;
- sr.sr_dma_max = sizeof(*irp);
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
-
- /**************************************************************************
- * NOTE: It turns out that the following routine might not be all that
- * safe to call. The man page for sg (the generic scsi driver)
- * mentions that it's "extremely hazardous" to bind the generic
- * scsi device (using SGIOCSTL) to a device that some other
- * SCSI driver is already using. I don't know if doing just a
- * inquiry command is hazardous, but this does sometimes cause
- * the machine to freeze up for short period of time when it's
- * doing the inquiry command on the bootup disk.
- *
- * Someone who knows more about SCSI programming than I do will
- * need to look this over, I guess. Garance Drosehn/Mar 19/94
- */
- int do_inquiryall(int replyCount, struct inquiry_all_reply *all,
- struct esense_reply *erp)
- {
- struct scsi_adr scsiAddress;
- struct inquiry_reply *thisIrp;
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err, index, maxIndex, diskIndex, tryCount;
- int tempFd = 0;
-
- /* Open up one of the generic SCSI device drivers */
- tempFd = open("/dev/sg0", O_RDONLY );
- if (tempFd == -1) tempFd = open("/dev/sg1", O_RDONLY );
- if (tempFd == -1)
- return 0; /* failed */
-
- /* for now, assume there's only one SCSI controller with
- * 8 devices (id's 0 thru 7, where 7 is the computer).
- * I make this assumption because I have no idea what
- * to do if there are multiple SCSI controllers...
- */
- maxIndex = replyCount;
- if ( replyCount > INQ_ALL_MAX_SCSI_CNT )
- maxIndex = INQ_ALL_MAX_SCSI_CNT;
- all->maxScsiCount = maxIndex;
-
- /* Go through all the devices in the /dev directory */
- diskIndex = 0;
- for(index = 0; index < maxIndex; index++) {
- bzero((char *)&scsiAddress, sizeof(scsiAddress));
-
- /* Set the scsi address */
- scsiAddress.sa_target = index;
- scsiAddress.sa_lun = 0;
-
- /* Tell the driver what device we want to talk to */
- err = ioctl(tempFd, SGIOCSTL, &scsiAddress);
-
- /* Check for an error */
- if (err == -1) {
- close(tempFd);
- fatal("do_inquiryall: unable to set SCSI address");
- }
-
- thisIrp = &(all->scsiArray[index].inqResult);
- tryCount = 0;
- try_again:
- tryCount++;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_INQUIRY;
- c6p->c6_len = sizeof(*thisIrp);
-
- sr.sr_addr = (char *)thisIrp;
- sr.sr_dma_max = sizeof(*thisIrp);
- sr.sr_ioto = 90; /* timeout */
- sr.sr_dma_dir = SR_DMA_RD;
-
- /* this routine can't call do_inquiry here because it
- needs to do an SGIOCREQ instead of SDIOCREQ */
- err = ioctl(tempFd, SGIOCREQ, &sr);
- *erp = sr.sr_esense;
- all->scsiArray[index].devIoStat = sr.sr_io_status;
- all->scsiArray[index].deviceNumber = -1;
- switch (sr.sr_io_status) {
- case SR_IOST_GOOD:
- if(tryCount > 1) {
- printf("scsi-cmd debug: scsi_id= %d OK on try#=%d\n",
- index, tryCount);
- }
- all->scsiArray[index].deviceNumber = diskIndex++;
- break;
- case SR_IOST_SELTO:
- /* it seems we get a "selection timeout" if there is
- * no device with this scsi ID. Do Nothing */
- break;
- case SR_IOST_CHKSV:
- case SR_IOST_CHKSNV:
- /* if these come up, then there is a device here.
- * Retrying it often seems to be the cure, though
- * one should *-> NOTE <-* that I have no idea if
- * that is the right thing to do at this point...
- * Also NOTE that when this happens, the machine
- * tends to freeze up for about 10-20 seconds.
- */
- if (tryCount < 2) goto try_again;
- all->scsiArray[index].deviceNumber = diskIndex++;
- default:
- /* probably need to do something with any other
- * io_status values, but what? In some cases a
- * retry might make sense, or maybe incrementing
- * the disk index to at least skip over the device.
- * Perhaps a SGIOCRST to reset the scsi bus?
- */
- printf("scsi-cmd debug: scsi_id= %d, io_stat= %d try#=%d\n",
- index, sr.sr_io_status, tryCount);
- break;
- }
- }
-
- if (tempFd > -1) close(tempFd);
- return 1; /* seems to have succeeded */
- }
-
- int do_testunitready(int fd, struct timeval *tvp, struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_TESTRDY;
-
- sr.sr_addr = NULL;
- sr.sr_dma_max = 0;
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
-
- *tvp = sr.sr_exec_time;
-
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
- int do_modesense(int fd, struct mode_sense_reply *msrp, int page,
- struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct mode_sense_cmd *mscp;
- int err;
-
- bzero((char *)&sr, sizeof(sr));
-
- mscp = (struct mode_sense_cmd *)&sr.sr_cdb;
- mscp->msc_opcode = C6OP_MODESENSE;
- mscp->msc_pcf = 0; /* report current values */
- mscp->msc_page = page;
- mscp->msc_len = sizeof(*msrp);
-
- sr.sr_addr = (char *)msrp;
- sr.sr_dma_max = sizeof(*msrp);
- /*
- * Extend timeout so Quantum drive works with test.
- */
- sr.sr_ioto = 50;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
-
- printf("sr.sr_io_status: %d\n", sr.sr_io_status);
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
- int do_readcapacity(int fd, struct capacity_reply *crp,
- struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_10 *c10p;
- int err;
-
- bzero((char *)&sr, sizeof(sr.sr_cdb));
-
- c10p = (struct cdb_10 *)&sr.sr_cdb;
- c10p->c10_opcode = C10OP_READCAPACITY;
-
- sr.sr_addr = (char *)crp;
- sr.sr_dma_max = sizeof(*crp);
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
- int do_seek(int fd, int lba, struct timeval *tvp, struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_SEEK;
- #ifdef i386
- if (lba > 0) {
- c6p->c6_lba0 = lba & 0xFF;
- c6p->c6_lba1 = (lba >> 8) & 0xFF;
- c6p->c6_lba2 = (lba >> 16) & 0xFF;
- } else
- c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
- #else
- c6p->c6_lba = lba;
- #endif
-
- sr.sr_addr = 0;
- sr.sr_dma_max = 0; /* don't really do I/O to memory */
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
-
- *tvp = sr.sr_exec_time;
-
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
- int do_read(int fd, int lba, int nblks, struct timeval *tvp,
- struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err;
-
- if (nblks > 256)
- fatal("Too many blocks for read: %d", nblks);
- if (nblks == 256)
- nblks = 0;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_READ;
- #ifdef i386
- if (lba > 0) {
- c6p->c6_lba0 = lba & 0xFF;
- c6p->c6_lba1 = (lba >> 8) & 0xFF;
- c6p->c6_lba2 = (lba >> 16) & 0xFF;
- } else
- c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
- #else
- c6p->c6_lba = lba;
- #endif
- c6p->c6_len = nblks;
-
- sr.sr_addr = NULL;
- sr.sr_dma_max = 0; /* don't really do I/O to memory */
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_RD;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
-
- *tvp = sr.sr_exec_time;
-
- if (sr.sr_dma_xfr != 0)
- fatal("scsi driver did transfer: %d", sr.sr_dma_xfr);
-
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-
- int do_write(int fd, int lba, int nblks, struct timeval *tvp,
- struct esense_reply *erp)
- {
- struct scsi_req sr;
- struct cdb_6 *c6p;
- int err;
-
- if (nblks > 256)
- fatal("Too many blocks for read: %d", nblks);
- if (nblks == 256)
- nblks = 0;
-
- bzero((char *)&sr, sizeof(sr));
-
- c6p = (struct cdb_6 *)&sr.sr_cdb;
- c6p->c6_opcode = C6OP_WRITE;
- #ifdef i386
- if (lba > 0) {
- c6p->c6_lba0 = lba & 0xFF;
- c6p->c6_lba1 = (lba >> 8) & 0xFF;
- c6p->c6_lba2 = (lba >> 16) & 0xFF;
- } else
- c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
- #else
- c6p->c6_lba = lba;
- #endif
- c6p->c6_len = nblks;
-
- sr.sr_addr = NULL;
- sr.sr_dma_max = 0; /* don't really do I/O to memory */
- sr.sr_ioto = 5;
- sr.sr_dma_dir = SR_DMA_WR;
-
- err = ioctl(fd, SDIOCSRQ, &sr);
-
- *tvp = sr.sr_exec_time;
-
- if (sr.sr_dma_xfr != 0)
- fatal("scsi driver did transfer: %d", sr.sr_dma_xfr);
-
- *erp = sr.sr_esense;
- return err | sr.sr_io_status;
- }
-